﻿using DotNetCommon.Data;
using NUnit.Framework;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotNetCommon.Extensions;
using System.Diagnostics;
using System.Threading;
using Newtonsoft.Json;
using DotNetCommon.Serialize;
using System.Collections.ObjectModel;

namespace DotNetCommon.Test.Extensions
{
    [TestFixture]
    public class ObjectTests_Mapper
    {
        #region 不同数据类型的转换
        public class ClassType
        {
            #region 可空 <=> 非可空
            //int => int?
            public int Int2IntNull { get; set; }
            //int => int?
            public int? IntNull2Int { get; set; }
            #endregion

            #region string => int
            public string String2Int { get; set; }
            public string String2IntNullValue { get; set; }
            public string String2IntNull { get; set; }
            #endregion

            #region other =>string
            //int => string
            public int Int2String { get; set; }
            //int? => string
            public int? IntNull2String { get; set; }
            //double => string
            public double Double2String { get; set; }
            public double? DoubleNull2String { get; set; }
            #endregion

            #region double => int & int => long & int=>double
            //double => int
            public double Double2Int { get; set; }
            //int => long
            public int Int2Long { get; set; }
            //int => double
            public int Int2Double { get; set; }
            #endregion

            #region datetime <=> string
            //datetime => string(使用DateTime标明的格式)
            [MapperArguments("yyyy-MM-dd HH:mm:ss.fff")]
            public DateTime DateTime2String { get; set; }

            //datetime? => string(使用DateTime标明的格式)
            [MapperArguments("yyyy-MM-dd HH:mm:ss.fff")]
            public DateTime? DateTimeNull2String { get; set; }

            //datetime => string 没有格式化字符串
            public DateTime DateTime2StringNoFormatter { get; set; }

            //datetime => string(使用string标明的格式)
            public DateTime DateTime2StringStringPropFormatter { get; set; }

            //string => datetime(使用DateTime标明的格式)
            public string String2DateTime { get; set; }

            //string => datetime?
            public string String2DateTimeNull { get; set; }

            //string => datetime?
            public string String2DateTimeNullNoFormatter { get; set; }

            #endregion

            #region enum <=> string & enum <=> int
            //enum => string
            public EnumTest Enum2String { get; set; }
            //enum? => string
            public EnumTest? EnumNull2String { get; set; }
            //string => enum
            public string String2Enum { get; set; }

            //enum =>  int
            public EnumTest Enum2Int { get; set; }
            //int => enum
            public int Int2Enum { get; set; }
            public EnumTest? EnumNull2Int { get; set; }
            #endregion

            #region bool <=> string
            //bool => string
            public bool Bool2String { get; set; }
            //boolnull => string
            public bool? BoolNull2String { get; set; }
            //string => bool
            public string String2Bool { get; set; }
            //string => bool?
            public string String2BoolNull { get; set; }
            #endregion

            #region string <=> guid
            public string String2Guid { get; set; }

            [MapperArguments("N")]
            public Guid Guid2String { get; set; }
            [MapperArguments("N")]
            public Guid? GuidNull2String { get; set; }
            #endregion

            #region arr/IEnumerable <=> list
            //arr => arr/list/ienumerable/Collection
            public int?[] IntNullArr2EnumerableIntNull { get; set; }
            public int?[] IntNullArr2EnumerableString { get; set; }
            public int?[] IntNullArr2ListString { get; set; }
            public int?[] IntNullArr2ListInt { get; set; }
            public long[] LongArr2IntArr { get; set; }
            public long[] LongArr2StringList { get; set; }
            public long[] LongArr2ListIntNull { get; set; }

            //arr => collection
            public string[] StringArr2ICollectionInt { get; set; }
            public string[] StringArr2ICollectionIntNull { get; set; }
            public List<string> StringList2ICollectionInt { get; set; }
            public int?[] IntNullArr2ICollectionInt { get; set; }
            public long[] LongArr2ListICollectionIntNull { get; set; }

            public Guid[] GuidArr2ListString { get; set; }
            public Guid?[] GuidNullArr2ListString { get; set; }
            public IEnumerable<string> StringEnum2ListGuid { get; set; }
            public IEnumerable<string> StringEnum2ListGuidNull { get; set; }

            //string[] => List<string> 
            public string[] StringArr2ListString { get; set; }
            public List<string> ListString2ListInt { get; set; }
            #endregion
        }

        public class ClassTypeDto
        {
            #region 可空 & 非可空
            public int? Int2IntNull { get; set; }
            public int IntNull2Int { get; set; }
            #endregion

            #region string => int
            public int String2Int { get; set; }
            public int String2IntNullValue { get; set; }
            public int? String2IntNull { get; set; }
            #endregion

            #region other =>string
            public string Int2String { get; set; }
            public string IntNull2String { get; set; }
            public string Double2String { get; set; }
            public string DoubleNull2String { get; set; }
            #endregion

            #region double => int & int => long & int=>double
            public long Int2Long { get; set; }
            public double Int2Double { get; set; }
            public int Double2Int { get; set; }
            #endregion

            #region datetime <=> string
            [MapperArguments("yyyy-MM-dd")]
            public string DateTime2String { get; set; }
            public string DateTimeNull2String { get; set; }
            public string DateTime2StringNoFormatter { get; set; }
            [MapperArguments("yyyy-MM-dd HH:mm:ss.fff")]
            public string DateTime2StringStringPropFormatter { get; set; }

            [MapperArguments("yyyy-MM-dd HH:mm:ss.fff")]
            public DateTime String2DateTime { get; set; }
            [MapperArguments("yyyy-MM-dd HH:mm:ss.fff")]
            public DateTime? String2DateTimeNull { get; set; }

            public DateTime String2DateTimeNullNoFormatter { get; set; }
            #endregion

            #region enum <=> string & enum <=> int
            public string Enum2String { get; set; }
            public string EnumNull2String { get; set; }
            public EnumTest String2Enum { get; set; }

            public int Enum2Int { get; set; }
            public EnumTest Int2Enum { get; set; }
            public int EnumNull2Int { get; set; }
            #endregion

            #region bool <=> string
            //bool => string
            public string Bool2String { get; set; }
            //boolnull => string
            public string BoolNull2String { get; set; }
            //string => bool
            public bool String2Bool { get; set; }
            //string => bool?
            public bool? String2BoolNull { get; set; }
            #endregion

            #region string <=> guid
            [MapperArguments("N")]
            public Guid String2Guid { get; set; }
            [MapperArguments("N")]
            public string Guid2String { get; set; }
            [MapperArguments("N")]
            public string GuidNull2String { get; set; }
            #endregion

            #region arr/IEnumerable <=> list
            public IEnumerable<int?> IntNullArr2EnumerableIntNull { get; set; }
            public IEnumerable<string> IntNullArr2EnumerableString { get; set; }
            public List<string> IntNullArr2ListString { get; set; }
            public List<int> IntNullArr2ListInt { get; set; }
            public int[] LongArr2IntArr { get; set; }
            public List<string> LongArr2StringList { get; set; }
            public List<int?> LongArr2ListIntNull { get; set; }

            //arr => collection
            public ICollection<int> StringArr2ICollectionInt { get; set; }
            public ICollection<int?> StringArr2ICollectionIntNull { get; set; }
            public ICollection<int> StringList2ICollectionInt { get; set; }
            public ICollection<int> IntNullArr2ICollectionInt { get; set; }
            public ICollection<int?> LongArr2ListICollectionIntNull { get; set; }

            public List<string> GuidArr2ListString { get; set; }
            public List<string> GuidNullArr2ListString { get; set; }
            public List<Guid> StringEnum2ListGuid { get; set; }
            public List<Guid?> StringEnum2ListGuidNull { get; set; }

            public List<string> StringArr2ListString { get; set; }
            public List<int> ListString2ListInt { get; set; }
            #endregion
        }
        [Test]
        public void TestDiffrentType()
        {
            var guid = Guid.NewGuid();
            var classA = new ClassType
            {
                #region 可空 & 非可空
                Int2IntNull = 1,
                IntNull2Int = null,
                #endregion

                #region string => int
                String2Int = "123",
                String2IntNullValue = null,
                String2IntNull = null,
                #endregion

                #region other =>string
                Int2String = 23,
                IntNull2String = null,
                Double2String = 12.34,
                DoubleNull2String = null,
                #endregion

                #region double => int & int => long & int=>double
                Int2Long = 2,
                Int2Double = 3,
                Double2Int = 4.6,
                #endregion

                #region datetime <=> string
                DateTime2String = DateTime.Parse("2022-01-10 17:11:00.123"),
                DateTimeNull2String = null,
                DateTime2StringNoFormatter = DateTime.Parse("2022-01-10 17:11:00.123"),
                DateTime2StringStringPropFormatter = DateTime.Parse("2022-01-10 17:11:00.123"),
                String2DateTime = "2022-01-10 17:11:00.123",
                String2DateTimeNull = null,
                String2DateTimeNullNoFormatter = null,
                #endregion

                #region enum <=> string & enum <=> int
                Enum2String = EnumTest.C,
                String2Enum = "C",
                EnumNull2String = null,
                Enum2Int = EnumTest.C,
                Int2Enum = 2,
                EnumNull2Int = null,
                #endregion

                #region bool <=> string
                Bool2String = true,
                BoolNull2String = null,
                String2Bool = "true",
                String2BoolNull = null,
                #endregion

                #region string <=> guid
                String2Guid = guid.ToString(),
                Guid2String = guid,
                GuidNull2String = null,
                #endregion

                #region arr/IEnumerable <=> list
                IntNullArr2EnumerableString = new int?[] { 1, null, 2 },
                IntNullArr2ListString = new int?[] { 1, null, 2 },
                IntNullArr2EnumerableIntNull = new int?[] { 1, null, 2 },
                IntNullArr2ListInt = new int?[] { 1, null, null },
                LongArr2IntArr = new long[] { 1, 12 },
                LongArr2StringList = new long[] { 1, 12 },
                LongArr2ListIntNull = new long[] { 1, 2 },

                //arr => collection
                StringArr2ICollectionInt = new string[] { "1", null },
                StringArr2ICollectionIntNull = new string[] { "1", null },
                StringList2ICollectionInt = new List<string> { "1", null, "2" },
                IntNullArr2ICollectionInt = new int?[] { 1, null },
                LongArr2ListICollectionIntNull = new long[] { 1, 2 },

                GuidArr2ListString = new Guid[] { guid },
                GuidNullArr2ListString = new Guid?[] { guid, null },
                StringEnum2ListGuid = new string[] { guid.ToString() },
                StringEnum2ListGuidNull = new string[] { guid.ToString(), null },

                StringArr2ListString = new string[] { "1", "3" },
                ListString2ListInt = new List<string>() { "1", "2" }
                #endregion
            };
            var res = classA.Mapper<ClassTypeDto>();
            Test(res);
            Thread.Sleep(5000);
            res = classA.Mapper<ClassTypeDto>();
            Test(res);
            void Test(ClassTypeDto dto)
            {
                dto.ShouldNotBeNull();

                #region 可空 & 非可空
                dto.Int2IntNull.ShouldBe(1);
                dto.IntNull2Int.ShouldBe(0);
                #endregion

                #region string => int
                dto.String2Int.ShouldBe(123);
                dto.String2IntNullValue.ShouldBe(0);
                dto.String2IntNull.ShouldBeNull();
                #endregion

                #region other =>string
                dto.Int2String.ShouldBe("23");
                dto.IntNull2String.ShouldBeNull();
                dto.Double2String.ShouldBe("12.34");
                dto.DoubleNull2String.ShouldBeNull();
                #endregion

                #region double => int & int => long & int=>double
                dto.Int2Long.ShouldBe(2);
                dto.Int2Double.ShouldBe(3);
                dto.Double2Int.ShouldBe(5);
                #endregion                

                #region datetime <=> string
                dto.DateTime2String.ShouldBe("2022-01-10 17:11:00.123");
                dto.DateTimeNull2String.ShouldBeNull();
                dto.DateTime2StringNoFormatter.ShouldBe(DateTime.Parse("2022-01-10 17:11:00.123").ToString());
                dto.DateTime2StringStringPropFormatter.ShouldBe("2022-01-10 17:11:00.123");
                dto.String2DateTime.ShouldBe(DateTime.Parse("2022-01-10 17:11:00.123"));
                dto.String2DateTimeNull.ShouldBeNull();
                dto.String2DateTimeNullNoFormatter.ShouldBe(default(DateTime));
                #endregion

                #region enum <=> string & enum <=> int
                dto.Enum2String.ShouldBe("C");
                dto.String2Enum.ShouldBe(EnumTest.C);
                dto.EnumNull2String.ShouldBeNull();
                dto.Enum2Int.ShouldBe(2);
                dto.Int2Enum.ShouldBe(EnumTest.C);
                dto.EnumNull2Int.ShouldBe(0);
                #endregion

                #region bool <=> string
                dto.Bool2String.ShouldBe("True");
                dto.BoolNull2String.ShouldBeNull();
                dto.String2Bool.ShouldBe(true);
                dto.String2BoolNull.ShouldBeNull();
                #endregion

                #region string <=> guid
                dto.String2Guid.ShouldBe(guid);
                dto.Guid2String.ShouldBe(guid.ToString("N"));
                dto.GuidNull2String.ShouldBeNull();
                #endregion

                #region arr/IEnumerable <=> list
                dto.IntNullArr2EnumerableString.ShouldBe(new string[] { "1", null, "2" });
                dto.IntNullArr2ListString.ShouldBe(new string[] { "1", null, "2" });
                dto.IntNullArr2EnumerableIntNull.ShouldBe(new int?[] { 1, null, 2 });
                dto.IntNullArr2ListInt.ShouldBe(new int[] { 1, 0, 0 });
                dto.LongArr2IntArr.ShouldBe(new int[] { 1, 12 });
                dto.LongArr2StringList.ShouldBe(new string[] { "1", "12" });
                dto.LongArr2ListIntNull.ShouldBe(new int?[] { 1, 2 });

                //arr => collection
                dto.StringArr2ICollectionInt.ShouldBe(new int[] { 1, 0 });
                dto.StringArr2ICollectionIntNull.ShouldBe(new int?[] { 1, null });
                dto.StringList2ICollectionInt.ShouldBe(new int[] { 1, 0, 2 });
                dto.IntNullArr2ICollectionInt.ShouldBe(new int[] { 1, 0 });
                dto.LongArr2ListICollectionIntNull.ShouldBe(new int?[] { 1, 2 });


                dto.GuidArr2ListString.ShouldBe(new string[] { guid.ToString() });
                dto.GuidNullArr2ListString.ShouldBe(new string[] { guid.ToString(), null });
                dto.StringEnum2ListGuid.ShouldBe(new Guid[] { guid });
                dto.StringEnum2ListGuidNull.ShouldBe(new List<Guid?> { guid, null });

                dto.StringArr2ListString.ShouldBe(new string[] { "1", "3" });
                dto.ListString2ListInt.ShouldBe(new List<int>() { 1, 2 });
                #endregion

            }
        }
        #endregion

        #region 简单树状结构
        /// <summary>
        /// 简单树状结构
        /// </summary>
        [Test]
        public void TestMapper()
        {
            List<Org> list = null;
            #region 构造平铺的数据
            list = new List<Org>()
            {
                new Org()
                {
                    Id=1,
                    Name="河南",
                    Pwd="henan",
                    CreatetTime=DateTime.Now,
                    Active=null
                },
                new Org()
                {
                    Id=2,
                    Name="河北",
                    Pwd="hebei",
                    CreatetTime=DateTime.Now,
                    Active=null
                },
                new Org()
                {
                    Id=3,
                    Name="郑州",
                    ParentId=1,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="zhengzhou"
                },
                new Org()
                {
                    Id=4,
                    Name="开封",
                    ParentId=1,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="kaifeng"
                },
                new Org()
                {
                    Id=5,
                    Name="中原区",
                    ParentId=3,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="zhongyuanqu"
                },
                new Org()
                {
                    Id=6,
                    Name="金水区",
                    ParentId=3,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="jinshuiqu"
                },
                new Org()
                {
                    Id=7,
                    Name="文化路",
                    ParentId=6,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="wenhualu"
                }
            };
            #endregion
            var orgs = list.FetchToTree(o => o.Id, o => o.ParentId);

            var str = "";
            var st = new Stopwatch();
            for (var i = 0; i < 10; i++)
            {
                Thread.Sleep(1000);
                st.Restart();
                var dtos = orgs.Mapper<List<OrgDto>>();

                dtos.ShouldNotBeNull();
                dtos.Count.ShouldBe(2);
                dtos[0].Name.ShouldBe("河南");
                dtos[1].Name.ShouldBe("河北");
                dtos[0].Children.ShouldNotBeNull();
                dtos[0].Children.Count.ShouldBe(2);
                dtos[0].Children[0].Children[1].Name.ShouldBe("金水区");
                dtos[0].Children[0].Children[1].Children[0].Name.ShouldBe("文化路");
                st.Stop();
                str += $"耗时: {st.ElapsedMilliseconds} 毫秒\r\n";
            }
            int k = 0;

        }
        #endregion

        #region 外层是集合
        [Test]
        public void TestList()
        {
            RoslynMapper.RegisterConvert<Org, OrgDto>();
            Thread.Sleep(3000);

            var list = new List<Org>()
            {
                new Org
                {
                    Id = 1,
                    Active = true,
                    CreatetTime = DateTime.Now,
                    Name ="1",
                    ParentId = 1,
                }
            };
            var str = "";
            for (var i = 0; i < 10; i++)
            {
                var st = new Stopwatch();
                st.Start();
                for (int j = 0; j < 10000; j++)
                {
                    var res = list.Mapper<List<OrgDto>>();
                }
                str += $"耗时: {st.ElapsedMilliseconds}毫秒\r\n";
                st.Stop();
                //Thread.Sleep(2000);
            }
            var i2 = 0;
        }

        #endregion

        #region 循环引用
        /// <summary>
        /// 处理循环引用
        /// </summary>
        [Test]
        public void TestMapperCircle()
        {
            List<PersonCircle> list = new List<PersonCircle>()
            {
                new PersonCircle()
                {
                    Id=1,
                    Name="小明",
                    Pwd="xiaopming",
                    Book=new BookCircle()
                    {
                        Id=1,
                        Name="语文"
                    }
                },
                new PersonCircle()
                {
                    Id=1,
                    Name="小王",
                    Pwd="wang",
                    Book=new BookCircle()
                    {
                        Id=2,
                        Name="数学"
                    }
                }
            };
            list[0].Book.Person = list[0];
            list[1].Book.Person = list[1];
            var dtos = list.Mapper<List<PersonCircleDto>>();
            dtos.ShouldNotBeNull();
            dtos.Count.ShouldBe(2);
            dtos[0].Name.ShouldBe("小明");
            dtos[0].Book.Person.ShouldBe(dtos[0]);
            dtos[1].Name.ShouldBe("小王");
            dtos[1].Book.Person.ShouldBe(dtos[1]);
            Thread.Sleep(2000);
            dtos = list.Mapper<List<PersonCircleDto>>();
            dtos.ShouldNotBeNull();
            dtos.Count.ShouldBe(2);
            dtos[0].Name.ShouldBe("小明");
            dtos[0].Book.Person.ShouldBe(dtos[0]);
            dtos[1].Name.ShouldBe("小王");
            dtos[1].Book.Person.ShouldBe(dtos[1]);
        }
        #endregion

        #region 简单循环
        [Test]
        public void TestSimpleCircle()
        {
            var code = new RoslynMapper().GenerateMapCode(typeof(Dog), typeof(DogDto)).Code;
            var dog = new Dog()
            {
                Id = 1,
                Name = "小王"
            };
            dog.Self = dog;
            var dto = dog.Mapper<DogDto>();
            dto.ShouldNotBeNull();
            dto.Name.ShouldBe("小王");
            dto.Self.ShouldNotBeNull();
            dto.Self.ShouldBe(dto);
            Thread.Sleep(2000);
            dto = dog.Mapper<DogDto>();
            dto.ShouldNotBeNull();
            dto.Name.ShouldBe("小王");
            dto.Self.ShouldNotBeNull();
            dto.Self.ShouldBe(dto);
        }
        #endregion

        #region 简单类型集合转换
        [Test]
        public void TestCollect()
        {
            var collect = new Collect()
            {
                Id = 1,
                Name = "小王",
                Ids = new List<int>() { 4, 6 },
                Names = new List<string>() { "A", "B" },
                Scores = new List<float>() { 101.12f, 98.12f }
            };
            var dto2 = collect.Mapper<CollectDto>();
            Test(dto2);
            Thread.Sleep(2000);
            dto2 = collect.Mapper<CollectDto>();
            Test(dto2);
            void Test(CollectDto dto)
            {
                dto.ShouldNotBeNull();
                dto.Name.ShouldBe("小王");
                dto.Ids.ShouldNotBeNull();
                dto.Ids.Count.ShouldBe(2);
                dto.Ids[1].ShouldBe(6);

                dto.Names.ShouldNotBeNull();
                dto.Names.ToList().Count.ShouldBe(2);
                dto.Names.ToList()[1].ShouldBe("B");

                dto.Scores.ShouldNotBeNull();
                dto.Scores.Count.ShouldBe(2);
                dto.Scores[1].ShouldBe(98.12f);
            }

        }
        #endregion

        #region 复杂类型集合转换
        [Test]
        public void TestComplextCollect()
        {
            var col = new Collect2()
            {
                Id = 1,
                Name = "测试",
                Subs = new List<Collect2Sub>()
                {
                    new Collect2Sub()
                    {
                        Id=1,
                        Name="sub1",
                        Score=12
                    },
                     new Collect2Sub()
                    {
                        Id=2,
                        Name="sub2",
                        Score=88
                    }
                },
                SubsEnumerable = new List<Collect2Sub>()
                {
                    new Collect2Sub()
                    {
                        Id=1,
                        Name="sub3",
                        Score=12
                    },
                     new Collect2Sub()
                    {
                        Id=2,
                        Name="sub4",
                        Score=88
                    }
                },
                SubsIList = new List<Collect2Sub>()
                {
                    new Collect2Sub()
                    {
                        Id=1,
                        Name="sub5",
                        Score=12
                    },
                     new Collect2Sub()
                    {
                        Id=2,
                        Name="sub6",
                        Score=88
                    }
                },
                CollectSubArr = new List<Collect2Sub>()
                {
                    new Collect2Sub()
                    {
                        Id=1,
                        Name="sub5",
                        Score=12
                    },
                     new Collect2Sub()
                    {
                        Id=2,
                        Name="sub6",
                        Score=88
                    }
                }.ToArray()
            };
            var dto = col.Mapper<Collect2Dto>();
            dto.ShouldNotBeNull();
            dto.Name.ShouldBe("测试");
            dto.Subs.ShouldNotBeNull();
            dto.Subs.Count.ShouldBe(2);

            dto.SubsEnumerable.ShouldNotBeNull();
            dto.SubsEnumerable.ToList().Count.ShouldBe(2);
            dto.SubsEnumerable.ToList()[0].Name.ShouldBe("sub3");

            dto.SubsIList.ShouldNotBeNull();
            dto.SubsIList.Count.ShouldBe(2);
            dto.SubsIList[0].Name.ShouldBe("sub5");

            dto.CollectSubArr.ShouldNotBeNull();
            dto.CollectSubArr.Length.ShouldBe(2);
            dto.CollectSubArr[1].Name.ShouldBe("sub6");

        }
        #endregion

        #region 只读属性
        [Test]
        public void TestGetProp()
        {
            var cat = new Cat()
            {
                Id = 1,
                Name = "小明",
                Birth = DateTime.Parse("1989-01-02"),
                Age = 20
            };
            var dto = cat.Mapper<CatDto>();
            dto.ShouldNotBeNull();
            dto.Id.ShouldBe(1);
            dto.Name.ShouldBe("小明");
            dto.Age.ShouldNotBe(20);
        }
        #endregion

        #region 继承的属性
        [Test]
        public void TestExtendProp()
        {
            var zi = new Zi()
            {
                Id = 1,
                Name = "小明",
                Addr = "天明路",
                Pwd = "123",
                Score = 99
            };
            var dto = zi.Mapper<ZiDto>();
        }
        #endregion

        #region 父子类 忽略继承关系

        [Test]
        public void TestFuZi()
        {
            var zi = new TestZi()
            {
                Id = 1,
                Name = "TestZi"
            };
            var fu = zi.Mapper<TestFu>();
            fu.ShouldNotBeNull();
            var b = fu is TestZi;
            b.ShouldBeFalse();
            fu.Id.ShouldBe(1);

            Thread.Sleep(2000);
            fu = zi.Mapper<TestFu>();
            fu.ShouldNotBeNull();
            b = fu is TestZi;
            b.ShouldBeFalse();
            fu.Id.ShouldBe(1);
        }


        public class TestFu
        {
            public int Id { get; set; }
        }

        public class TestZi : TestFu
        {
            public string Name { get; set; }
        }

        #endregion

        public class ClassA
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public DateTime Birth { get; set; }
            public List<string> Books { get; set; }
        }

        public class ClassB
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public DateTime Birth { get; set; }
            public List<string> Books { get; set; }
        }

        [Test]
        public void Test()
        {
            RoslynMapper.EventRoslynMapperTriggered += (ctx, result) =>
            {
                result.Success.ShouldBeTrue();
            };
            var a = new ClassA()
            {
                Id = 1,
                Name = "ClassA",
                Birth = DateTime.Parse("2021-05-25T21:11:18.8078273+08:00"),
                Books = new List<string>() { "语文", "数学" }
            };
            ClassB b = a.Mapper<ClassB>();
            //{"Id":1,"Name":"ClassA","Birth":"2021-05-25T21:11:18.8078273+08:00","Books":["语文","数学"]}
            var json = b.ToJson();
            json.ShouldBe("{\"Id\":1,\"Name\":\"ClassA\",\"Birth\":\"2021-05-25T21:11:18.8078273+08:00\",\"Books\":[\"语文\",\"数学\"]}");

            Thread.Sleep(3000);
            b = a.Mapper<ClassB>();

            //{"Id":1,"Name":"ClassA","Birth":"2021-05-25T21:11:18.8078273+08:00","Books":["语文","数学"]}
            json = b.ToJson();
            json.ShouldBe("{\"Id\":1,\"Name\":\"ClassA\",\"Birth\":\"2021-05-25T21:11:18.8078273+08:00\",\"Books\":[\"语文\",\"数学\"]}");
        }
    }

    #region 简单树状结构模型
    public class Org : ITreeStruct<Org>
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Pwd { get; set; }
        public DateTime CreatetTime { get; set; }
        public bool? Active { get; set; }
        public int? ParentId { get; set; }

        public List<Org> Children { get; set; }
    }

    public class OrgDto : ITreeStruct<OrgDto>
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool? Active { get; set; }

        public List<OrgDto> Children { get; set; }
    }
    #endregion

    #region 循环引用
    public class PersonCircle
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Pwd { get; set; }
        public BookCircle Book { set; get; }
    }

    public class BookCircle
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public PersonCircle Person { get; set; }
    }

    public class PersonCircleDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public BookCircleDto Book { set; get; }
    }

    public class BookCircleDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public PersonCircleDto Person { get; set; }
    }
    #endregion

    #region 简单循环

    public class Dog
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Dog Self { get; set; }
    }

    public class DogDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DogDto Self { get; set; }
    }

    #endregion

    #region 简单类型集合转换
    public class Collect
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<int> Ids { get; set; }
        public IEnumerable<string> Names { get; set; }
        public IList<float> Scores { get; set; }
        public string[] Flags { get; set; }
    }

    public class CollectDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<int> Ids { get; set; }
        public IEnumerable<string> Names { get; set; }
        public IList<float> Scores { get; set; }
        public string[] Flags { get; set; }
    }
    #endregion

    #region 复杂类型集合转换
    public class Collect2
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Collect2Sub> Subs { get; set; }
        public IEnumerable<Collect2Sub> SubsEnumerable { get; set; }
        public IList<Collect2Sub> SubsIList { get; set; }
        public Collect2Sub[] CollectSubArr { get; set; }
    }
    public class Collect2Sub
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Score { get; set; }
    }

    public class Collect2Dto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Collect2SubDto> Subs { get; set; }
        public IEnumerable<Collect2SubDto> SubsEnumerable { get; set; }
        public IList<Collect2SubDto> SubsIList { get; set; }
        public Collect2SubDto[] CollectSubArr { get; set; }
    }
    public class Collect2SubDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Score { get; set; }
    }



    #endregion

    #region 只读属性
    public class Cat
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime Birth { get; set; }
    }

    public class CatDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age
        {
            get
            {
                return DateTime.Now.Year - Birth.Year;
            }
        }
        public DateTime Birth { get; set; }
    }
    #endregion

    #region 继承属性
    public class Fu
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Score { get; set; }
        public string Pwd { get; set; }
    }

    public class Zi : Fu
    {
        public string Addr { get; set; }
    }

    public class ZiDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Score { get; set; }
        public string Addr { get; set; }
    }
    #endregion    
}
